const reg = {
	// 不能直接return /\{\{((?:.|\n)+?)\}\}/g 会被认作是string
	text: () => new RegExp('\{\{((?:.|\n)+?)\}\}', 'g'),
	bind: () => new RegExp(':(.+)', 'g'),
	event: () => new RegExp('@(.+)', 'g'),
}
const utils = {
	safeGet: (o, path) => {
		try {
		    return path.split('.').reduce((o, k) => o[k], o)
		  } catch (e) {
		    return undefined
		  }
	},
	random: () => `v${Math.ceil(Math.random()*100000000)}`
}
const safeGet = utils.safeGet, random = utils.random

class Compiler{
	constructor({data, methods}) {
		this.data = data
		this.methods = methods
		this.elMap = new Map()
		this.eventMap = new Map()
	}
	init (el) {
		const fragment = Compiler.node2Fragment(el)
		const {allEl, textEl, formEl} = Compiler.getElements(fragment)
		textEl.forEach( (el) => {
			Compiler.compileTextElement(el, (key, {el, text}) => { Compiler.setMap(this.elMap, key, {el, text, nodeType:'text'}) })
		})
		formEl.forEach( (el) => {
			Compiler.compileFormElement(el, (key, {el}) => Compiler.setMap(this.elMap, key, {el, nodeType:'form'}) )
		})
		allEl.forEach( (el) => {
			Compiler.compileEvent(this.methods, el, (key, {el, eventType}) => this.eventMap.set(key, {el, eventType}))
		})
		console.log(this.elMap)
		el.parentNode.appendChild(fragment)
	}
	render(path, newVal) {
		const doms = this.elMap.get(path) || []
		doms.forEach(dom => {
			if(dom && dom.nodeType === 'text') dom.el.textContent = dom.text.replace(reg.text(), () => newVal)
			if(dom && dom.nodeType === 'form') dom.el.value = newVal
		})
	}
	onDataChange(path, newVal) {
		this.render(path, newVal)
	}
	static setMap(map, key, val) {
		const vals = map.get(key) || []
		vals.push(val)
		map.set(key, vals)
	}
	static node2Fragment (el) {
		let fragment = document.createDocumentFragment(), child;
		while (child = el.firstChild) {
		    fragment.appendChild(child);
		}
		return fragment
	}
	static getElements (el) {
		let childNodes = el.childNodes
		let elObj = { allEl:[], textEl:[], formEl: [] }
		// console.log(el, el.nodeType, el.nodeName, el.childNodes.length)
		if(childNodes.length > 1)
			Array.from(childNodes).forEach(node => {
				const {allEl, textEl, formEl} = Compiler.getElements(node)
				elObj.textEl.push(...textEl)
				elObj.formEl.push(...formEl)
				elObj.allEl.push(...allEl)
			})
		else if(childNodes.length === 1 && el.nodeType === 1) elObj.textEl.push(el)
		else if(childNodes.length === 0 && el.nodeName === 'INPUT') elObj.formEl.push(el)
		if(el.nodeType === 1) elObj.allEl.push(el)
		return elObj
	}
	static compileTextElement (el, addElMap) {
		let text = el.textContent
		if(reg.text().test(text)) {
			// 给dom做上标记
			const id = random()
			el.setAttribute('id', id)
			// 加入domMap
			text.replace(reg.text(), (m, $1) => addElMap($1, {el, text}) )
		}
	}
	static compileFormElement (el, addElMap) {
		let attrs = el.attributes
		Array.from(attrs).forEach(attr => {
			if(!reg.bind().test(attr.name)) return
			attr.name.replace(reg.bind(), (m, $1) => {
				if($1 !== 'value') return
				const value = attr.value
				addElMap(value, {el, value})
			})
		})
	}
	static compileEvent (methods, el, addEventMap) {
		let attrs = el.attributes
		Array.from(attrs).forEach(attr => {
			if(!reg.event().test(attr.name)) return
			attr.name.replace(reg.event(), (m, $1) => {
				const event = methods[attr.value]
				el.addEventListener( $1, event, false)
			})
		})
	}
}

function compile({el, data, methods}){
	const compiler = new Compiler({data, methods})
	compiler.init(el)
	return compiler
}



